fix(chat): make theming actually work (0.0.5)#166
Merged
Merged
Conversation
The 0.0.4 architecture set design tokens on every chat component's
:host. Two consequences that broke the documented theming contract:
1. Direct-on-host token settings shadow CSS inheritance from :root,
regardless of specificity. So a consumer's
`:root { --ngaf-chat-primary: blue; }` (specificity 0,0,1) was
ignored — `[_nghost-c123] { --ngaf-chat-primary: dark; }` (0,1,0,
targeting the chat element directly) won.
2. `:host([data-ngaf-chat-theme="dark"])` only matched the component's
own host, not ancestors. And every child component (chat-input,
chat-message, chat-window, ...) re-set light tokens on its own
:host, shadowing parent attribute switches anyway.
Fix: stop setting tokens on :host. Inject the defaults onto `:root`
once via a shared `<style id="ngaf-chat-root-tokens">` element,
appended to <head> on first chat-component module evaluation
(idempotent, SSR-guarded). Wrap the rule set in `@layer ngaf-chat`
so consumer's unlayered `:root { ... }` wins regardless of source
order — the standard CSS pattern for framework defaults.
Theme switching now works on any ancestor: setting
`[data-ngaf-chat-theme="dark"]` on `<html>`, `<body>`, or any wrapper
flips dark via inherited custom properties (no more :host override
plumbing needed).
Validated end-to-end against the published smoke app:
- Default light tokens load on :root ✓
- `:root { --ngaf-chat-primary: rgb(80, 0, 200); }` consumer override
applies to chat element (chatPrimary becomes rgb(80, 0, 200)) ✓
- `document.body.setAttribute('data-ngaf-chat-theme', 'dark')` flips
the entire chat to dark ✓
Bumps @ngaf/chat to 0.0.5.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Theming was advertised but didn't work. Smoke-test investigation against the published @ngaf/chat@0.0.4 surfaced two related bugs:
Consumer's
:root { --ngaf-chat-primary: ...; }override was ignored. The 0.0.4 architecture set tokens on every chat component's:host. Direct-on-element token settings shadow CSS inheritance from ancestor:rootregardless of specificity. `[_nghost-c123] { --ngaf-chat-primary: dark; }` (set on the chat element) always won over `:root { --ngaf-chat-primary: blue; }` (inherited).[data-ngaf-chat-theme=\"dark\"]attribute didn't switch dark mode. `:host([data-ngaf-chat-theme="dark"])` only matched the component's own host. Setting the attribute on ``/``/any wrapper had no effect. Even setting it on `` directly, child chat components (chat-input, chat-message, chat-window, ...) re-set light tokens on their own :host and shadowed it anyway.Fix
Stop setting tokens on `:host`. Inject the defaults onto `:root` once via a shared `<style id="ngaf-chat-root-tokens">` element, appended to `` on first chat-tokens module evaluation (idempotent, SSR-guarded).
Wrap the default rules in `@layer ngaf-chat` so consumer's unlayered `:root { --ngaf-chat-*: ... }` wins regardless of source order — the standard CSS pattern for framework defaults.
```ts
// libs/chat/src/lib/styles/chat-tokens.ts
const ROOT_TOKEN_STYLES = `
@layer ngaf-chat {
:root { --ngaf-chat-primary: rgb(28, 28, 28); /* ...defaults... / }
@media (prefers-color-scheme: dark) {
:root { --ngaf-chat-primary: rgb(255, 255, 255); / ... / }
}
:root[data-ngaf-chat-theme="dark"],
[data-ngaf-chat-theme="dark"] { / dark tokens / }
/ ...etc... */
}
`;
export function ensureChatRootStyles(): void {
if (typeof document === 'undefined') return;
if (document.getElementById('ngaf-chat-root-tokens')) return;
const style = document.createElement('style');
style.id = 'ngaf-chat-root-tokens';
style.textContent = ROOT_TOKEN_STYLES;
document.head.appendChild(style);
}
ensureChatRootStyles(); // auto-invoke on module evaluation
```
Validation
End-to-end against the smoke app:
Test plan
🤖 Generated with Claude Code